package com.biz.primus.ms.payment.service;

import com.biz.primus.common.utils.JsonUtils;
import com.biz.primus.model.payment.enums.AllInPayPayResultCode;
import com.biz.primus.model.payment.enums.AllInPayTrxStatusCode;
import com.biz.primus.model.payment.vo.req.AllInPayH5PayReqVo;
import com.biz.primus.model.payment.vo.req.PaymentCancelReqVo;
import com.biz.primus.model.payment.vo.req.PaymentRefundReqVo;
import com.biz.primus.model.payment.vo.req.PaymentReqVo;
import com.biz.primus.model.payment.vo.resp.PaymentCancelResp;
import com.biz.primus.model.payment.vo.resp.PaymentRefundRespVo;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.payment.channel.allinpay.AllInPayPayFactory;
import com.biz.primus.ms.payment.channel.allinpay.config.AllinPayConfig;
import com.biz.primus.ms.payment.channel.allinpay.lang.RSA;
import com.biz.primus.ms.payment.channel.allinpay.req.*;
import com.biz.primus.ms.payment.channel.allinpay.res.*;
import com.biz.primus.ms.payment.channel.wechat.exceptions.PaymentException;
import com.biz.primus.ms.payment.dao.po.AllInPayCancelLogPo;
import com.biz.primus.ms.payment.dao.repository.AllInPayCancelLogRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * AllInPayPaymentService
 *
 * @author JKLiues
 * @date 2017年11月10日
 */
@Service
@Slf4j
public class AllInPayPaymentService extends AbstractBaseService {

    @Value("${primus.tl.cer}")
    private String CER_PATH;

    @Value("${primus.tl.pfx}")
    private String PFX_PATH;

    @Value("${primus.tl.pwd}")
    private String TL_PWD;

    @Value("${primus.tl.alias}")
    private String TL_ALIAS;

    private final AllInPayCancelLogRepository allInPayCancelLogRepository;

    @Autowired
    public AllInPayPaymentService(AllInPayCancelLogRepository allInPayCancelLogRepository) {
        this.allInPayCancelLogRepository = allInPayCancelLogRepository;
    }

    /**
     * 生成预付单
     *
     * @param allInPayPaymentReqVo 请求参数封装对象
     */
    public AllInPayPayRespVo createPayment(PaymentReqVo allInPayPaymentReqVo) {
        String errorMsg;
        try {
            AllInPayPayFactory allInPayPayFactory = AllInPayPayFactory.newInstance();
            AllInPayUnifiedOrder unifiedOrder = allInPayPayFactory.newUnifiedOrder(allInPayPaymentReqVo);
            String sign = allInPayPayFactory.newSigner().sign(unifiedOrder.getSignParams(), unifiedOrder);
            return unifiedOrder.execute(sign);
        } catch (Exception e) {
            log.error("生成预付单失败.", e);
            errorMsg = e.getMessage();
        }
        throw new PaymentException(errorMsg);
    }


    /**
     * 退款
     */
    public PaymentRefundRespVo refund(PaymentRefundReqVo reqVo) {
        String errorMsg;
        try {
            AllInPayPayFactory allInPayPayFactory = AllInPayPayFactory.newInstance();
            AllInPayRefund orderRefund = allInPayPayFactory.newOrderRefund(reqVo);
            String sign = AllInPayPayFactory.newInstance().newSigner().sign(orderRefund.getSignParams(), orderRefund);
            AllInPayRefundRespVo refundResponse = orderRefund.execute(sign);
            if (refundResponse.isProcessSuccess() &&
                    Objects.equals(refundResponse.getTrxstatus(), AllInPayTrxStatusCode.SUCCESS.getCode())) {
                PaymentRefundRespVo respVo = new PaymentRefundRespVo();
                respVo.setProcessSuccess(true);
                respVo.setRefundApplySuccess(true);
                respVo.setOutRefundNo(reqVo.getRefundNo());
                return respVo;
            } else {
                errorMsg = refundResponse.getErrmsg();
            }
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.error("通联申请退款失败，退款单:{}.{}", reqVo, e);
            }
            errorMsg = e.getMessage();
        }
        throw new PaymentException(errorMsg);
    }

    /**
     * 撤销支付
     */
    public PaymentCancelResp cancel(PaymentCancelReqVo reqVo) {
        AllInPayPayFactory allInPayPayFactory = AllInPayPayFactory.newInstance();
        AllInPayCancel allInPayCancel = allInPayPayFactory.newOrderCancel(reqVo);
        String sign = allInPayPayFactory.newSigner().sign(allInPayCancel.getSignParams(), allInPayCancel);
        AllInPayCancelRespVo execute = allInPayCancel.execute(sign);
        PaymentCancelResp resp = new PaymentCancelResp();
        if (execute.isProcessSuccess()) {
            String trxstatus = execute.getTrxstatus();
            AllInPayTrxStatusCode statusCode = AllInPayTrxStatusCode.valueOfCode(trxstatus);
            resp.setTrxStatus(statusCode);
        } else {
            log.error("撤销支付失败：{}", execute.getErrmsg());
            resp.setTrxStatus(AllInPayTrxStatusCode.FAILED);
            resp.setErrMsg(execute.getErrmsg());
        }
        this.cancelLog(execute);
        return resp;
    }


    /**
     * 查询退款
     */

    public PaymentRefundRespVo refundQuery(PaymentRefundReqVo reqVo) {
        String errorMsg;
        try {
            AllInPayPayFactory allInPayPayFactory = AllInPayPayFactory.newInstance();
            AllInPayQuery query = allInPayPayFactory.newQuery(reqVo);
            String sign = allInPayPayFactory.newSigner().sign(query.getSignParams(), query);
            AllInPayQueryRespVo queryResponse = query.execute(sign);
            if (queryResponse.isProcessSuccess()) {
                PaymentRefundRespVo paymentRefundRespVo = parseRefundQueryResponse(queryResponse);
                paymentRefundRespVo.setOutRefundNo(reqVo.getRefundNo());
                return paymentRefundRespVo;
            } else {
                errorMsg = queryResponse.getErrmsg();
            }
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.error("查询退款失败.", e);
            }
            errorMsg = e.getMessage();
        }
        throw new PaymentException(errorMsg);
    }


    /**
     * 获取通联H5支付参数
     */
    public AllInPayH5PayReqVo buildH5Pay(PaymentReqVo paymentReqVo) {
        AllInPayPayFactory allInPayPayFactory = AllInPayPayFactory.newInstance();
        AllInPayH5PayReqVo h5Pay = allInPayPayFactory.newH5Pay(paymentReqVo);
        String encrypt = RSA.encrypt(PFX_PATH, TL_PWD, TL_ALIAS, new AllInPayH5Pay().getSignParams(), h5Pay);
        h5Pay.setSignMsg(encrypt);
        return h5Pay;
    }

    /**
     * 通联卡云支付
     */
    public AllInPayCardPayPwdRespVo createPaymentCard(PaymentReqVo paymentReqVo) {
        AllInPayPayFactory allInPayPayFactory = AllInPayPayFactory.newInstance();
        AllInPayCardPay cardPay = allInPayPayFactory.newCardPay(paymentReqVo);
        String sign = allInPayPayFactory.newSigner().signWithKey2(cardPay.getSignParams(), cardPay, AllinPayConfig.APP_SECRET);
        return cardPay.execute(sign);
    }

    /**
     * 通联反扫支付
     */
    public AllInPayScanPayRespVo createPaymentScan(PaymentReqVo paymentReqVo) {
        AllInPayPayFactory allInPayPayFactory = AllInPayPayFactory.newInstance();
        AllInPayScanPay scanPay = allInPayPayFactory.newScanPay(paymentReqVo);
        String sign = allInPayPayFactory.newSigner().sign(scanPay.getSignParams(), scanPay);
        return scanPay.execute(sign);
    }

    /**
     * 通联卡云退款
     */
    public PaymentRefundRespVo refundCard(PaymentRefundReqVo reqVo) {
        AllInPayPayFactory allInPayPayFactory = AllInPayPayFactory.newInstance();
        AllInPayCardRefund cardRefund = allInPayPayFactory.newCardBack(reqVo);
        String sign = allInPayPayFactory.newSigner().signWithKey2(cardRefund.getSignParams(), cardRefund, AllinPayConfig.APP_SECRET);
        AllInPayCardBackDataRespVo execute = cardRefund.execute(sign);
        AllInPayCardBackRespVo result = execute.getPpcs_cardback_add_response();
        PaymentRefundRespVo respVo = new PaymentRefundRespVo();
        if (result != null) {
            respVo.setProcessSuccess(true);
            respVo.setRefundSuccess(true);
        } else {
            respVo.setProcessSuccess(true);
            respVo.setRefundSuccess(false);
        }
        return respVo;
    }

    /**
     * 通联卡云退款查询
     */
    public PaymentRefundRespVo refundCardQuery(PaymentRefundReqVo reqVo) {
        AllInPayPayFactory allInPayPayFactory = AllInPayPayFactory.newInstance();
        AllInPayCardRefundQuery cardRefund = allInPayPayFactory.newCardBackQuery(reqVo);
        String sign = allInPayPayFactory.newSigner().signWithKey2(cardRefund.getSignParams(), cardRefund, AllinPayConfig.APP_SECRET);
        AllInPayCardBackQueryDataRespVo result = cardRefund.execute(sign);
        PaymentRefundRespVo respVo = new PaymentRefundRespVo();
        if (result != null) {
            respVo.setProcessSuccess(true);
            respVo.setRefundSuccess(true);
        } else {
            respVo.setProcessSuccess(true);
            respVo.setRefundSuccess(false);
        }
        return respVo;
    }


    private void cancelLog(AllInPayCancelRespVo execute) {
        AllInPayCancelLogPo logPo = new AllInPayCancelLogPo();
        logPo.setFinTime(execute.getFintime());
        logPo.setReqSn(execute.getReqsn());
        logPo.setTrxId(execute.getTrxid());
        logPo.setTrxStatus(execute.getTrxstatus());
        logPo.setId(idService.getNextId());
        logPo.setJsonStr(JsonUtils.obj2Json(execute));
        allInPayCancelLogRepository.save(logPo);
    }

    private PaymentRefundRespVo parseRefundQueryResponse(AllInPayQueryRespVo queryResponse) {
        AllInPayPayResultCode code = queryResponse.getRetcode();
        PaymentRefundRespVo paymentRefundRespVo = new PaymentRefundRespVo();
        if (code == AllInPayPayResultCode.FAIL) {
            paymentRefundRespVo.setRefundApplySuccess(false);
            return paymentRefundRespVo;
        }
        String status = queryResponse.getTrxstatus();
        AllInPayTrxStatusCode statusCode = AllInPayTrxStatusCode.valueOfCode(status);
        paymentRefundRespVo.setRefunding(Objects.equals(statusCode, AllInPayTrxStatusCode.PROCESSING));
        paymentRefundRespVo.setRefundSuccess(Objects.equals(statusCode, AllInPayTrxStatusCode.SUCCESS));
        paymentRefundRespVo.setRetryAble(Objects.equals(statusCode, AllInPayTrxStatusCode.TIMEOUT));
        return paymentRefundRespVo;
    }
}